/*
 * @Author       : mark
 * @Date         : 2020-06-25
 * @copyleft Apache 2.0
 */
#ifndef HTTP_REQUEST_H
#define HTTP_REQUEST_H

#include <unordered_map>
#include <unordered_set>
#include <string>
#include <regex>
#include <errno.h>
#include <mysql/mysql.h> //mysql

#include "../buffer/buffer.h"
#include "../log/log.h"
#include "../pool/sqlconnpool.h"
#include "../pool/sqlconnRAII.h"

class HttpRequest
{
public:
    enum PARSE_STATE
    {
        REQUEST_LINE,
        HEADERS,
        BODY,
        FINISH,
    };

    enum HTTP_CODE
    {
        NO_REQUEST = 0,
        GET_REQUEST,
        BAD_REQUEST,
        NO_RESOURSE,
        FORBIDDENT_REQUEST,
        FILE_REQUEST,
        INTERNAL_ERROR,
        CLOSED_CONNECTION,
    };

    HttpRequest() { Init(""); }
    ~HttpRequest() = default;

    void Init(const std::string &srcDir);
    bool parse(Buffer &buff);

    std::string path() const;
    std::string &path();
    std::string method() const;
    std::string version() const;
    std::string GetPost(const std::string &key) const;
    std::string GetPost(const char *key) const;

    bool IsKeepAlive() const;

    /* 
    todo 
    void HttpConn::ParseFormData() {}
    void HttpConn::ParseJson() {}
    */

private:
    bool ParseRequestLine_(const std::string &line);
    void ParseHeader_(const std::string &line);
    void ParseBody_(const std::string &line);

    void ParsePath_();
    void ParsePost_();
    void ParseFromUrlencoded_();

    static bool UserVerify(const std::string &name, const std::string &pwd, bool isLogin);

    PARSE_STATE state_;
    std::string method_, path_, version_, body_, srcDir_;
    std::unordered_map<std::string, std::string> header_;
    std::unordered_map<std::string, std::string> post_;

    static const std::unordered_set<std::string> DEFAULT_HTML;
    static const std::unordered_map<std::string, int> DEFAULT_HTML_TAG;
    static int ConverHex(char ch);
};

class UrlCode
{
public:
    void urlEncode(std::string &src, std::string &dst)
    {
        char cSrc[src.length() + 1];
        char cDst[src.length() * 3 + 1];
        memset(cSrc, 0, src.length() + 1);
        strncpy(cSrc, src.c_str(), src.length() + 1);
        url_encode(cSrc, cDst);
        dst = cDst;
    }
    void urlDecode(std::string &src, std::string &dst)
    {
        char cSrc[src.length() + 1];
        char cDst[src.length() * 3 + 1];
        memset(cSrc, 0, src.length() + 1);
        strncpy(cSrc, src.c_str(), src.length() + 1);
        url_decode(cSrc, cDst);
        dst = cDst;
    }
    /* Converts a hex character to its integer value */
    char from_hex(char ch)
    {
        return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10;
    }

    /* Converts an integer value to its hex character*/
    char to_hex(char code)
    {
        static char hex[] = "0123456789abcdef";
        return hex[code & 15];
    }

    /* Returns a url-encoded version of str */
    /* IMPORTANT: be sure to free() the returned string after use */
    void url_encode(char *str, char *dst)
    {
        char *pstr = str;
        // char *buf = (char*)malloc(strlen(str) * 3 + 1);
        char *pbuf = dst;
        while (*pstr)
        {
            if (isalnum(*pstr) || *pstr == '-' || *pstr == '_' || *pstr == '.' || *pstr == '~')
                *pbuf++ = *pstr;
            else if (*pstr == ' ')
                *pbuf++ = '+';
            else
                *pbuf++ = '%', *pbuf++ = to_hex(*pstr >> 4), *pbuf++ = to_hex(*pstr & 15);
            pstr++;
        }
        *pbuf = '\0';
    }

    /* Returns a url-decoded version of str */
    /* IMPORTANT: be sure to free() the returned string after use */
    void url_decode(char *str, char *dst)
    {
        char *pstr = str;
        // char *buf = (char*)malloc(strlen(str) + 1);
        char *pbuf = dst;
        while (*pstr)
        {
            if (*pstr == '%')
            {
                if (pstr[1] && pstr[2])
                {
                    *pbuf++ = from_hex(pstr[1]) << 4 | from_hex(pstr[2]);
                    pstr += 2;
                }
            }
            else if (*pstr == '+')
            {
                *pbuf++ = ' ';
            }
            else
            {
                *pbuf++ = *pstr;
            }
            pstr++;
        }
        *pbuf = '\0';
    }
};

#endif //HTTP_REQUEST_H